1   /*
2    * Copyright (C) 2007 The Guava Authors
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package com.google.common.collect;
18  
19  import com.google.common.annotations.GwtCompatible;
20  import com.google.common.base.Preconditions;
21  
22  import java.util.Set;
23  
24  import javax.annotation.Nullable;
25  
26  /**
27   * Implementation of {@link ImmutableSet} with exactly one element.
28   *
29   * @author Kevin Bourrillion
30   * @author Nick Kralevich
31   */
32  @GwtCompatible(serializable = true, emulated = true)
33  @SuppressWarnings("serial") // uses writeReplace(), not default serialization
34  final class SingletonImmutableSet<E> extends ImmutableSet<E> {
35  
36    final transient E element;
37    // This is transient because it will be recalculated on the first
38    // call to hashCode().
39    //
40    // A race condition is avoided since threads will either see that the value
41    // is zero and recalculate it themselves, or two threads will see it at
42    // the same time, and both recalculate it.  If the cachedHashCode is 0,
43    // it will always be recalculated, unfortunately.
44    private transient int cachedHashCode;
45  
46    SingletonImmutableSet(E element) {
47      this.element = Preconditions.checkNotNull(element);
48    }
49  
50    SingletonImmutableSet(E element, int hashCode) {
51      // Guaranteed to be non-null by the presence of the pre-computed hash code.
52      this.element = element;
53      cachedHashCode = hashCode;
54    }
55  
56    @Override
57    public int size() {
58      return 1;
59    }
60  
61    @Override public boolean isEmpty() {
62      return false;
63    }
64  
65    @Override public boolean contains(Object target) {
66      return element.equals(target);
67    }
68  
69    @Override public UnmodifiableIterator<E> iterator() {
70      return Iterators.singletonIterator(element);
71    }
72  
73    @Override boolean isPartialView() {
74      return false;
75    }
76  
77    @Override
78    int copyIntoArray(Object[] dst, int offset) {
79      dst[offset] = element;
80      return offset + 1;
81    }
82  
83    @Override public boolean equals(@Nullable Object object) {
84      if (object == this) {
85        return true;
86      }
87      if (object instanceof Set) {
88        Set<?> that = (Set<?>) object;
89        return that.size() == 1 && element.equals(that.iterator().next());
90      }
91      return false;
92    }
93  
94    @Override public final int hashCode() {
95      // Racy single-check.
96      int code = cachedHashCode;
97      if (code == 0) {
98        cachedHashCode = code = element.hashCode();
99      }
100     return code;
101   }
102 
103   @Override boolean isHashCodeFast() {
104     return cachedHashCode != 0;
105   }
106 
107   @Override public String toString() {
108     String elementToString = element.toString();
109     return new StringBuilder(elementToString.length() + 2)
110         .append('[')
111         .append(elementToString)
112         .append(']')
113         .toString();
114   }
115 }